#include <iostream>
#include "log.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <typeinfo>
using namespace std;

int test1()
{}

int test2()
{}

int test3()
{}


constexpr int pow(int base, int exp) noexcept
{
return (exp == 0 ? 1 : base * pow(base, exp - 1));
}

constexpr int pow2(int base, int exp) noexcept
{
    /*
    if (exp == 0 )
    {
        auto a = 3;
       // return 1;
    }
*/

   return  base * pow(base, exp - 1);


}
class WidgetFrag{
    public:
    WidgetFrag()
    {
        logInfo("WidgetFrag::construct:");
    }
    ~WidgetFrag()
    {
        logInfo("WidgetFrag::detroy:");
    }
/*
    WidgetFrag(WidgetFrag& rhs) noexcept// move constructor
    {
       logInfo("WidgetFrag::copy constructor");
    }

    WidgetFrag& operator=(WidgetFrag& rhs) noexcept// move assignment operator
    {
        logInfo("WidgetFrag::copy operator");
    }

    WidgetFrag(WidgetFrag&& rhs) // move constructor
    {
        logInfo("WidgetFrag::move constructor");
    }

    WidgetFrag& operator=(WidgetFrag&& rhs)// move assignment operator
    {
        logInfo("WidgetFrag::move operator");
    }
*/
};



class Widget {
public:
    Widget() // move constructor
    {
        logInfo("Widget::constructor");
    }
    ~Widget()
    {
        logInfo("Widget::detroy:");
    }



    Widget(Widget& rhs) // move constructor
    {
       logInfo("Widget::copy constructor");



    }

    Widget& operator=(Widget& rhs)// move assignment operator
    {
        logInfo("Widget::copy operator");
    }

    Widget(Widget&& rhs) // move constructor
    {
        logInfo("Widget::move constructor");


    }

    Widget& operator=(Widget&& rhs)// move assignment operator
    {
        logInfo("Widget::move operator");
    }


    int a=1;
    int b{2};
    int c{3};
    int d{4};
    char str1[64]="abcdefg";
    char *str2;
    std::string str3="ABCDEFG";
    WidgetFrag frag1;

   /*
    void fun1()
    {
        logInfo("fun1                 .");
    }
    */


    void fun1() &
    {
        logInfo("fun1 & & & &");
    }

    void fun1() &&
    {
        logInfo("fun1 && && && &&");
    }
};

Widget *getWidget1()
{
    Widget w;
    w.a = 201;
    w.b = 202;
    w.c = 203;
    return &w;
}

Widget getWidget2()
{
    Widget w;
    w.a = 301;
    w.b = 302;
    w.c = 303;
    return w;
}

Widget& getWidget3()
{
    Widget w;
    w.a = 3301;
    w.b = 3302;
    w.c = 3303;
    Widget &w1 = w;
    return w1;
}
static Widget getWidget4()
{
    Widget w;
    w.a = 301;
    w.b = 302;
    w.c = 303;
    return w;
}

void process(const Widget& lvalArg) // process lvalues
{
    logInfo("process:& & &");
}

void process(Widget&& rvalArg) // process rvalues
{
    logInfo("process: && && && &&");
}

template<typename T> // template that passes
void logAndProcess(T&& param) // param to process
{
//auto now = // get current time
//std::chrono::system_clock::now();
//makeLogEntry("Calling 'process'", now);
//process(std::forward<T>(param));
    process(param);
}

template<typename T> // template that passes
void logAndProcess2(T&& param) // param to process
{
//auto now = // get current time
//std::chrono::system_clock::now();
//makeLogEntry("Calling 'process'", now);
//process(std::forward<T>(param));
    process(std::forward<T>(param));
}
template<typename T>
void f(T&& param) // param is a universal reference
{
    //T a;
    std::string str = typeid(param).name();
    std::string str2 = typeid(T).name();

    logInfo("f:type=[%s][%s]", str.c_str(), str2.c_str());
}


int modercpp_main()
{
    cout << "Hello World!" << endl;
    Widget w1;
    logInfo("Widget w1():[%d,%d,%d,%d]", w1.a, w1.b, w1.c, w1.d);

    logInfo("Widget w2(w1);");
    Widget w2(w1);
    w2.a = 21;
    logInfo("Widget w3 = w1;");
    Widget w3 = w1;
    logInfo("w3 = w2;");
    w3 = w2;
    logInfo("w3 = w1;");
    w3 = w1;
    w3.fun1();

    logInfo("************************************************");
    Widget *pw1 =  new Widget();
    pw1->a=101;
    pw1->b=102;
    pw1->c=103;
    delete pw1;

    logInfo("************************************************");
    logInfo(" getWidget2();");
    getWidget2().fun1();
    logInfo("************************************************");
    {
        logInfo("Widget w4 = getWidget2();");
        Widget&& w4 = getWidget2();
        w4.fun1();
        logInfo("=======================================");

    }
    logInfo("************************************************");


    logInfo("************************************************");
    logInfo(" getWidget3();");
    getWidget3().fun1();
    logInfo("************************************************");
    {
        logInfo("Widget w5 = getWidget3();");
       // Widget &w5 = getWidget3();
      //  int a= w5.a;
        logInfo("w5.a=%d", getWidget3().a);
        logInfo("=======================================");
       //

    }
    logInfo("************************************************");

    logInfo("************************************************");
    logInfo(" getWidget4();");
    getWidget4().fun1();
    logInfo("************************************************");
    {
        logInfo("Widget w4 = getWidget4();");
        Widget w4 = getWidget4();
        w4.fun1();
        logInfo("=======================================");
        logInfo("Widget w5");
        Widget w5;
        logInfo(" w5 = w4;");
        w5 = w4;
        logInfo("Widget w6 = w4;");
        Widget w6 = w4;
        logInfo("Widget w7;");
        Widget w7;
        logInfo(" w7 = getWidget4();");
        w7 = getWidget4();
        logInfo("=======================================");
    }
    logInfo("************************************************");
    Widget w11;
    logAndProcess(w11);
    logAndProcess(getWidget2());
    logInfo("************************************************");

    logAndProcess2(w11);
    logAndProcess2(getWidget2());


    logInfo("************************************************");
    Widget w;
    f(w); // lvalue passed to f; param's type is
    // Widget& (i.e., an lvalue reference)
    f(std::move(w)); // rvalue passed to f; param's type is
    // Widget&& (i.e., an rvalue reference)

    logInfo("************************************************");
    logInfo("************************************************");
    return 0;
}
